package client.controller;

import java.io.IOException;
import java.net.UnknownHostException;

import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;

import client.network.AlreadyAuthentifiedException;
import client.network.Client;
import client.network.MaxLoginTryException;
import client.ui.AuthentificationDialog;
import client.ui.ConnectionDialog;
import client.ui.Window;

/**
 * Le controleur principal de l'application.
 * C'est lui qui initialise l'interface graphique, les controleurs subsidiaires et exécute l'application.
 */
public class MainController {

	/**
	 * Instance de la fenêtre principale de l'application
	 */
	private Window _mainWindow;
	
	/**
	 * La boite de dialogue modale contenant les paramètres de connexion
	 */
	private ConnectionDialog _connectionDialog;
	
	/**
	 * La boite de dialogue modale contenant les identifiants de l'utilisateur
	 */
	private AuthentificationDialog _authentificationDialog;
	
	/**
	 * Client réseau de l'application
	 */
	private Client _client;
	
	/*
	 * Controleurs subsidiaires
	 */
	private B_browseFolderController _B_browseFolderController;
	private B_connectionController _B_connectionController;
	private B_synchronizeController _B_synchronizeController;
	private ExplorerClientController _ExplorerClientController;
	
	/**
	 * Instancie un nouveau controleur principal
	 */
	public MainController()
	{}
	
	/**
	 * Exécute l'application.
	 */
	public void runApplication()
	{
		// Pour éviter tout conflit inter-thread lors de la création de la fenêtre graphique
		// la javadoc recommande d'utiliser la méthode invokeLater de l'API swing.
		// Sinon on peut se retrouver avec des erreurs irréparables au lancement de l'application.
		SwingUtilities.invokeLater(new Runnable(){
			public void run(){
				initApp();
				launchApp();
			}
		});
	}
	
	/**
	 * Initialise l'application
	 */
	public void initApp()
	{
		initView();
		initLogic();
	}
	
	/**
	 * Initialise la fenêtre et les controleurs.
	 */
	protected void initView()
	{
		// Instanciation d'une nouvelle fenêtre principale
		_mainWindow = new Window();
		
		// Instanciation des boites modales
		_connectionDialog = new ConnectionDialog(_mainWindow);
		_authentificationDialog = new AuthentificationDialog(_mainWindow);
		
		// Instanciation des controlleurs
		_B_browseFolderController = new B_browseFolderController(this);
		_B_connectionController = new B_connectionController(this);
		_B_synchronizeController = new B_synchronizeController(this);
		_ExplorerClientController = new ExplorerClientController(this);
		
		// Ajout des listeners
		_mainWindow.get_B_chooseRootFolder().addActionListener(_B_browseFolderController);
		_mainWindow.get_B_connection().addActionListener(_B_connectionController);
		_mainWindow.get_B_synchronize().addActionListener(_B_synchronizeController);
		_mainWindow.get_clientExplorer().addTreeSelectionListener(_ExplorerClientController);
		
	}
	
	/**
	 * Initialise la logique client/serveur.
	 */
	protected void initLogic()
	{
		_client = new Client(this);
	}
	
	/**
	 * Effectue les opérations de lancement de l'application
	 */
	protected void launchApp()
	{
		// Demande de connexion
		connectToServer();
		// Connexion établie
		
		// Demande d'authentification
		authentifyToServer();
		// Authentification réussie
		
		// Démarrage des opérations asynchrones (commandes / fichiers)
		_client.startAsyncTasks();
		
		// Affichage de la boite de bienvenue
		showWelcomeBox();
		
		// Affichage de la fenêtre
		_mainWindow.setVisible(true);
	}
	
	/**
	 * Affiche un formulaire pour fournir les paramètres de connexion du serveur.
	 * Lorsque la connexion est établie, la méthode se termine.
	 * Si l'utilisateur abandonne, l'application est quittée.
	 */
	protected void connectToServer()
	{
		int port;
		boolean success = false;
		do
		{
			// L'utilisateur a cliqué sur OK
			if (_connectionDialog.showDialog() == JOptionPane.OK_OPTION)
			{
				// Un des champs est vide
				if (_connectionDialog.getPort() == null ||
						_connectionDialog.getAddress() == null || 
						_connectionDialog.getPort().equals("") || 
						_connectionDialog.getAddress().equals(""))
				{
					showErrorBox("L'adresse ou le numéro de port est vide : \n Veuillez saisir une valeur.");
				}
				// Les deux champs sont remplis
				else
				{
					try {
						port = Integer.parseInt(_connectionDialog.getPort());
						success = _client.tryConnect(_connectionDialog.getAddress(), port);
					}
					catch (UnknownHostException e) {
						showErrorBox("Echec lors de la connexion au serveur : \n" + e.getMessage());
						e.printStackTrace();
					}
					catch (IOException e) {
						showErrorBox("Echec lors de la connexion au serveur : \n" + e.getMessage());
						e.printStackTrace();
					}
					catch (NumberFormatException e) {
						showErrorBox("\"" + _connectionDialog.getPort() + "\" n'est pas un numéro de port valide");
						e.printStackTrace();
					}
				}
			}
			// L'utilisateur a cliqué sur annuler ou a demandé la fermeture de la fenêtre
			else
			{
				// (aucune ressource à libérer ou à fermer)
				// On quitte l'application
				System.exit(0);
			}
		} while (!success);
	}
	
	/**
	 * Affiche un formulaire pour valider l'authentification de l'utilisateur auprès du serveur.
	 */
	protected void authentifyToServer()
	{
		boolean success;
		do
		{
			success = false;
			
			// L'utilisateur a cliqué sur OK
			if (_authentificationDialog.ShowDialog() == JOptionPane.OK_OPTION)
			{
				// Un des champs est vide
				if (_authentificationDialog.getIdentifiant() == null
						|| _authentificationDialog.getPassword() == null
						|| _authentificationDialog.getIdentifiant().equals("")
						|| _authentificationDialog.getPassword().equals(""))
				{
					showErrorBox("L'identifiant ou le mot de passe est vide : \n Veuillez saisir une valeur.");
				}
				// Les deux champs sont remplis
				else
				{
					try {
						success = _client.tryAuthentify(_authentificationDialog.getIdentifiant(), _authentificationDialog.getPassword());
						if (!success)
						{
							showWarningBox("L'authentification a échouée : \n l'identifiant et/ou le mot de passe sont incorrects.");
						}
					} catch (IOException e) {
						showErrorBox("Echec lors de la connexion au serveur : \n" + e.getMessage());
						e.printStackTrace();
					} catch (AlreadyAuthentifiedException e) {
						showErrorBox("L'utilisateur \"" + _authentificationDialog.getIdentifiant() + "\" est déjà authentifié.");
						e.printStackTrace();
					} catch (MaxLoginTryException e) {
						showErrorBox("Le maximum de tentatives d'authentifications consécutives a été atteint (" + e.getMaximum() + " tentatives). \n" +
								"L'application va s'arrêter.");
						e.printStackTrace();
						System.exit(0);
					}
				}
			}
			// L'utilisateur a cliqué sur annuler ou a demandé la fermeture de la fenêtre
			else
			{
				// On ferme le client
				if (_client != null) _client.close();
				
				// On quitte l'application
				System.exit(0);
			}
		} while (!success);
	}
	
	/**
	 * Affiche une boite de dialogue pour notifier que l'utilisateur est accepté
	 */
	protected void showWelcomeBox()
	{
		showInformationBox("Bienvenue " + _client.get_id() + " !");
	}
	
	/**
	 * Affiche une boite de dialogue modale pour annoncer une erreur
	 * @param error
	 * Le message d'erreur à afficher dans la boite de dialogue
	 */
	protected void showErrorBox(String error)
	{
		JOptionPane.showMessageDialog(null, error, "Erreur", JOptionPane.ERROR_MESSAGE);
	}
	
	/**
	 * Affiche une boite de dialogue modale pour annoncer un message de mise en garde
	 * @param error
	 * Le message de mise en garde à afficher dans la boite de dialogue
	 */
	protected void showWarningBox(String warning)
	{
		JOptionPane.showMessageDialog(null, warning, "Attention", JOptionPane.WARNING_MESSAGE);
	}
	
	
	/**
	 * Affiche une boite de dialogue modale pour annoncer une information
	 * @param info
	 * Le message d'information à afficher dans la boite de dialogue
	 */
	protected void showInformationBox(String info)
	{
		JOptionPane.showMessageDialog(null, info, "Information", JOptionPane.INFORMATION_MESSAGE);
	}

	/**
	 * Obtient la fenêtre principale de l'application.
	 * @return
	 * L'instance de la fenêtre principale de l'application.
	 */
	public Window getMainWindow() {
		return _mainWindow;
	}

	/**
	 * Obtient le client logique principal de l'application.
	 * @return
	 * L'instance cliente chargée des communications réseaux.
	 */
	public Client getClient() {
		return _client;
	}
	
	/**
	 * Obtient la boite de dialogue modale pour la connexion.
	 * @return
	 * La boite de dialogue avec un formulaire de connexion.
	 */
	public ConnectionDialog getConnectionDialog() {
		return _connectionDialog;
	}
}
